//auto类型推导
//使用了 auto 关键字以后，编译器会在编译期间自动推导出变量的类型，这样我们就不用手动指明变量的数据类型

auto n = 10;	//其中auto为int类型
auto f = 12.8;	//其中auto为double类型
auto p = &n;	//int*
auto a = 10, b = 11;	//auto推导没有二义性，a=10推导出auto为int，后面b只能为int,否则报错

//常见错误
auto a;		//auto必须初始化，否则无法推导,会报错
void func(auto x) {};	//auto不能在函数参数中使用,因为定义函数的时候没有初始化
auto str[10]; 		//auto不能定义数组

//auto实际应用
//有的数据类型比较复杂书写起来很麻烦比如迭代器。我们可以在定义此类对象的时候使用auto
#include<vector>
#include<iostream>
using namespace std;
vector<vector<int> > v;
auto i = v.begin();	//此处auto即为vector<vector<int> > iterator:: 

//也可以用于声明函数指针
double func(int a, double b, const char* c, float d, short e, long f)
{
	cout << "a= " << a << ", b= " << b << ", c= " << c << ", d= " << d << ", e= " << e << ", f= " << f << endl;
	return 3.3;
}

int main()
{
	double (*pf)(int, double, const char*, float, short, long);	//声明函数指针，形参名可要可不要
	pf = func;	//指针指向func函数
	pf(1, 2, "hell0", 3, 4, 5);		//通过指针调用函数
	auto pf1 = func;	//pf1 和 pf可以得到同样的效果
}

//可以用于模板中声明变量
template<typename T1, typename T2>
auto func(T1 x, T2 y)->decltype(x+y)	//函数声明也只能用auto
{
	auto tmp = x + y;	//这里只能用auto 
	return tmp;
}
int main()
{
	double a = 5.5;
	int b = 3;
	cout << func<double, int>(a, b) << endl;
	return 0;
}






//decltype关键字
//用于查询表达式的数据类型，语法: decltype(expression) var;
//expression是表达式或函数，只会查询不会执行,返回一个数据类型，可以直接后面接变量名var
//1)如果expression是没有用括号括起来的标识符，则var的类型与该标识符的类型相同，包括const等限定符。
//2)如果expression是函数调用，则var的类型与函数的返回值类型相同(函数不能返回void，但可以返回void *
//3)如果expression是左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符，那么var的类型是expression的引用。
//4)如果上面的条件都不满足，则var的类型与expression的类型相同 :
int a = 5;
int& pa = a;	//a是引用int类型
decltype(pa) da;	//da也是引用int类型
decltype(a) b;	//b是int类型

//总之，decltype的类型要么与表达式相同要么是表达式的引用类型

//auto与decltype的区别
int func1() { return 0; }
decltype((func1)) da;	//不会执行func1()，不用初始化
auto db = func1();	//会执行func1() ,必须初始化




//函数后置返回类型(函数定义和声明都可以用)
// 也可以不用，auto会自动推导
int func(int x, double y) {};
//等同于
auto func(int x, double y) -> int {};

template<typename T1, typename T2>
auto func(T1 x, T2 y) -> decltype(x + y)	//不能写decltype func(T1 x, T2 y)
						//因为前面不在x，y作用域内，后面在
{
	auto tmp = x + y;	
	return tmp;
}
int main()
{
	double a = 5.5;
	int b = 3;
	cout << func<double, int>(a, b) << endl;
	return 0;
}







//注意当auto和lambda函数一起用时，使用auto类型说明符声明的遍历不能出现在其自身的初始值设定中
function<int(int, int)> dfs = [&](int i, int t)->int
{
    return dfs(i - 1, t);
};
//这里的dfs如果用auto来简化成下面这样:
auto dfs = [&](int i, int t)->int
{
    
    return  dfs(i - 1, t);
};
//编译时会报错。

//但如果还是想用auto的话，那么参数里就要带dfs的右值引用
auto dfs = [&](auto&& dfs, int i, int t)->int
{
    
    return  dfs(dfs, i - 1, t);
};
//这样就不会报错